﻿using cap_demo.Infrastructure.Extensions;
using DotNetCore.CAP;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace cap_demo.Infrastructure
{
    public class EFDbContext : DbContext, IUnitOfWork, ITransaction
    {
        protected IMediator _mediator;
        private ICapPublisher _capBus;

        public EFDbContext(DbContextOptions options, IMediator mediator, ICapPublisher capBus) : base(options)
        {
            _capBus = capBus;
            _mediator = mediator;
        }

        #region ITransaction接口

        private IDbContextTransaction _dbContextTransaction;

        public bool HasActiveTransaction => _dbContextTransaction != null;

        public Task<IDbContextTransaction> BeginTransactionAsync()
        {
            if (_dbContextTransaction != null)
                return null;
            _dbContextTransaction = Database.BeginTransaction(publisher: _capBus, autoCommit: false);
            return Task.FromResult(_dbContextTransaction);
        }

        public async Task CommitTransactionAsync(IDbContextTransaction transaction)
        {
            if (transaction == null) throw new ArgumentNullException(nameof(transaction));
            if (transaction != _dbContextTransaction) throw new InvalidOperationException($"Transaction {transaction.TransactionId} is not current");
            try
            {
                await SaveChangesAsync();
                transaction.Commit();
            }
            catch
            {
                RollbackTransaction();
                throw;
            }
            finally
            {
                _dbContextTransaction?.Dispose();
                _dbContextTransaction = null;
            }
        }

        public IDbContextTransaction GetCurrentTransaction() => _dbContextTransaction;

        public void RollbackTransaction()
        {
            try
            {
                _dbContextTransaction?.Rollback();
            }
            catch (Exception)
            {
            }
            finally
            {
                _dbContextTransaction?.Dispose();
                _dbContextTransaction = null;
            }
        }

        #endregion ITransaction接口


        #region IUnitOfWork接口
        /// <summary>
        /// 异步保存实体
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default)
        {
            var saveResult = await base.SaveChangesAsync(cancellationToken);
            var saveSuccessFlag = saveResult > 0;
            if (saveSuccessFlag)
                await _mediator.DispatchDomainEventAsync(this);
            return saveSuccessFlag;
        }

        #endregion IUnitOfWork接口


    }
}
